package services

import (
	"github.com/go-playground/validator/v10"
	"github.com/goravel/framework/contracts/http"
	"github.com/pkg/errors"
	"goravel/app/consts"
	"goravel/app/types"
)

type ApiService struct {
	Ctx http.Context
}

func NewApiService(ctx http.Context) *ApiService {
	return &ApiService{Ctx: ctx}
}

type ErrorOption func(*errorOptions)
type errorOptions struct {
	Code   int
	Errors []string
}

func WithCode(code int) ErrorOption {
	return func(err *errorOptions) {
		err.Code = code
	}
}

func WithErrors(errors []string) ErrorOption {
	return func(err *errorOptions) {
		err.Errors = errors
	}
}

func (result *ApiService) Validate(data any) ([]string, error) {
	validate := validator.New(validator.WithRequiredStructEnabled())
	err := validate.Struct(data)
	var errArray []string
	if err != nil {
		var validateErrs validator.ValidationErrors
		if errors.As(err, &validateErrs) {
			for _, e := range validateErrs {
				errArray = append(errArray, e.Error())
			}
		}
	}
	return errArray, err
}

func (result *ApiService) OK(msg string) http.Response {
	return result.Ctx.Response().Success().Json(http.Json{
		"code": consts.SuccessCode,
		"msg":  msg,
	})
}

func (result *ApiService) Error(code int, msg string, opts ...ErrorOption) http.Response {
	defaultOpts := &errorOptions{
		Errors: make([]string, 0),
	}
	for _, opt := range opts {
		opt(defaultOpts)
	}
	return result.Ctx.Response().Success().Json(http.Json{
		"code":   code,
		"msg":    msg,
		"errors": defaultOpts.Errors,
	})
}

func (result *ApiService) Error404(msg string) http.Response {
	return result.Ctx.Response().Success().Json(http.Json{
		"code": http.StatusNotFound,
		"msg":  msg,
	})
}

func (result *ApiService) Error500(msg string, opts ...ErrorOption) http.Response {
	defaultOpts := &errorOptions{
		Code:   http.StatusInternalServerError,
		Errors: make([]string, 0),
	}
	for _, opt := range opts {
		opt(defaultOpts)
	}
	return result.Ctx.Response().Success().Json(http.Json{
		"code":   defaultOpts.Code,
		"msg":    msg,
		"errors": defaultOpts.Errors,
	})
}

func (result *ApiService) Abort(code int, msg string) {
	result.Ctx.Request().AbortWithStatusJson(http.StatusOK, http.Json{
		"code": code,
		"msg":  msg,
	})
}

func (result *ApiService) Abort401(msg string) {
	result.Ctx.Request().AbortWithStatusJson(http.StatusOK, http.Json{
		"code": http.StatusUnauthorized,
		"msg":  msg,
	})
}

func (result *ApiService) Data(data any) http.Response {
	return result.Ctx.Response().Success().Json(http.Json{
		"code": consts.SuccessCode,
		"msg":  "",
		"data": data,
	})
}

func (result *ApiService) DataPage(data any, meta types.Meta) http.Response {
	return result.Ctx.Response().Success().Json(http.Json{
		"code": consts.SuccessCode,
		"msg":  "",
		"data": http.Json{
			"list": data,
			"meta": meta,
		},
	})
}
